use std::path::Path;

use download::{get, Downloadable, LibraryAllowed};
use model::version::Version;
use parse::Parse;

pub fn fix_search(sub_mat: &clap::ArgMatches) {
  let version = sub_mat.get_one::<String>("VERSION");
  let type_ = sub_mat.get_one::<String>("type").unwrap();
  let versions = get_version_manifest().versions;

  let versions = versions.iter().filter(|v| {
    (if version.is_some() { v.id.contains(version.unwrap()) } else { true })
      && v.type_.eq(type_)
  });

  for version in versions {
    println!("Remote Support Version: {}", version.id);
  }
}

pub fn fix_download(sub_mat: &clap::ArgMatches) {
  let game_dir = std::env::current_dir().unwrap().join(".minecraft");

  let version = sub_mat.get_one::<String>("VERSION").unwrap();
  let versions = get_version_manifest().versions;

  if let Some(version) = versions.iter().find(|v| v.id.eq(version)) {
    version.download(&game_dir).unwrap_or_else(|err| {
      eprintln!("Download Error:{}", err);
    });
  } else {
    eprintln!("Version:{} not found", version);
  }
}

pub fn fix_launch(sub_mat: &clap::ArgMatches) {
  let game_dir = std::env::current_dir().unwrap().join(".minecraft"); // CONF: change the dir name

  let lib_deps_dir = game_dir.join("libraries");
  let assets_dir = game_dir.join("assets");
  let version = sub_mat.get_one::<String>("VERSION").unwrap();

  let version_dir = game_dir.join("versions").join(version);

  let natives_dir = version_dir.join("natives");
  let config_path = version_dir.join(format!("{}.json", version));
  let version_path = version_dir.join(format!("{}.jar", version));

  if !version_path.exists() || !config_path.exists() {
    eprintln!("no versions path exists, maybe fix version: {}", version);
    return;
  }

  let version =
    &Version::parse(&std::fs::read_to_string(&config_path).unwrap()).unwrap();

  if !natives_dir.exists() {
    std::fs::create_dir_all(&natives_dir).unwrap();
  }

  for lib in &version.libraries {
    if lib.allowed() && lib.name.contains("natives") {
      extract_jar_deps(
        &lib_deps_dir.join(&lib.downloads.artifact.path),
        &natives_dir,
      );
    }
  }

  let classpath = format!(
    "{}{}",
    &version
      .libraries
      .iter()
      .map(|as_lib| {
        format!(
          "{}{}",
          lib_deps_dir.join(&as_lib.downloads.artifact.path).display(),
          if cfg!(windows) { ";" } else { ":" }
        )
      })
      .collect::<String>(),
    version_path.display()
  );

  // NOTE: you need jdk first
  std::process::Command::new("java")
    .current_dir(&game_dir)
    .arg(format!("-Djava.library.path={}", natives_dir.display()))
    .arg("-cp")
    .arg(classpath)
    .arg(&version.main_class)
    .arg("--username")
    .arg("Enaium")
    .arg("--version")
    .arg(&version.id)
    .arg("--gameDir")
    .arg(game_dir)
    .arg("--assetsDir")
    .arg(assets_dir)
    .arg("--assetIndex")
    .arg(&version.asset_index.id)
    .arg("--accessToken")
    .arg("0")
    .arg("--versionType")
    .arg("RMCL 0.1.0")
    .status()
    .unwrap();
}

fn extract_jar_deps(jar_file: &Path, dir: &Path) {
  let mut archive =
    zip::ZipArchive::new(std::fs::File::open(jar_file).unwrap()).unwrap();

  for i in 0..archive.len() {
    let mut entry = archive.by_index(i).unwrap();
    if entry.is_file() && !entry.name().contains("META-INF") {
      let mut name = entry.name();

      if name.contains("/") {
        name = &name[entry.name().rfind("/").unwrap() + 1..];
      }

      let path = dir.join(name);

      if path.exists() {
        std::fs::remove_file(&path).unwrap();
      }

      let mut file = std::fs::File::create(&path).unwrap();

      std::io::copy(&mut entry, &mut file).unwrap();
    }
  }
}

fn get_version_manifest() -> model::version_manifest::VersionManifest {
  // CONF: this is where the remote support mc version json file
  return get("https://launchermeta.mojang.com/mc/game/version_manifest.json")
    .unwrap()
    .json::<model::version_manifest::VersionManifest>()
    .unwrap();
}
